import React, { useCallback, useEffect, forwardRef, useImperativeHandle, useRef } from 'react';
import { render } from 'react-dom';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { drawBuffer, VSHADER_SOURCE, FSHADER_SOURCE } from './bvf';
import { initShaders } from './shader';
import styles from './index.module.less';

const Index = forwardRef((props, ref) => {
  const { className } = props;

  useImperativeHandle(ref, () => ({
    drawArray: (p, array, w, h, l) => {
      if (array) {
        drawDance(p, array, w, h);
      }
    },
  }));

  const containerRef = useRef(null);
  const canRef = useRef(null);

  const onResize = useCallback(() => {
    const { offsetHeight: height, offsetWidth: width } = containerRef.current || {};

    const can = React.createElement('canvas', {
      width,
      height,
      style: { background: '#90202020' },
      ref: canRef,
    });
    containerRef.current && render(can, containerRef.current);

    const gl = canRef.current.getContext('webgl');
    if (gl) {
      initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE);
      gl.viewport(0, 0, width, height);
      const widthHandle = gl.getUniformLocation(gl.program, 'width');
      const heightHandle = gl.getUniformLocation(gl.program, 'height');
      gl.uniform1f(widthHandle, width);
      gl.uniform1f(heightHandle, height);

      const x1H = gl.getUniformLocation(gl.program, 'x1');
      const x2H = gl.getUniformLocation(gl.program, 'x2');
      gl.uniform1f(x1H, 0);
      gl.uniform1f(x2H, width);
    }
  }, []);

  useEffect(() => {
    const ro = new ResizeObserver(onResize);
    containerRef.current && ro.observe(containerRef.current);
    return () => {
      containerRef.current && ro.unobserve(containerRef.current);
      ro.disconnect();
    };
  }, []);

  const bvbv = (d) => {
    return new Uint8Array(d.buffer);
  };

  const drawDance = (palette, data, width, height, l) => {
    if (canRef.current) {
      const gl = canRef.current.getContext?.('webgl');
      if (gl) {
        const lengthHandle = gl.getUniformLocation(gl.program, 'length');
        gl.uniform1f(lengthHandle, l);

        const paletteLoc = gl.getUniformLocation(gl.program, 'u_Palette');
        gl.uniform1i(paletteLoc, 0);

        const samplerLoc = gl.getUniformLocation(gl.program, 'u_Sampler');
        gl.uniform1i(samplerLoc, 1);

        gl.activeTexture(gl.TEXTURE0);
        const texture1 = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture1);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 256 * 2, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, palette);

        gl.activeTexture(gl.TEXTURE1);
        const texture2 = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture2);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, bvbv(data));

        drawBuffer(gl, [-1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0], 'a_Position');

        gl.deleteTexture(texture1);
        gl.deleteTexture(texture2);
      }
    }
  };

  const scaleX = useRef({ x1: 0, x2: 0 }).current;

  return (
    <div
      ref={containerRef}
      className={classnames(styles.root, className)}
      onWheel={(e) => {
        let s = 2;
        if (e.deltaY > 0) {
          s = 1 / s;
        }
        const { left, bottom, right, top } = containerRef.current.getBoundingClientRect();
        const size = { width: right - left, heigth: bottom - top };
        const { clientX, clientY } = e;
        const { x1, x2 } = scaleX;
        const pos = { x: ((clientX - left) / size.width) * (x2 - x1) + x1, y: clientY - top };
        let x1h = pos.x - (pos.x - x1) / s;
        let x2h = pos.x - (pos.x - (x2 || size.width)) / s;
        if (x1h < 0) {
          x1h = 0;
        }
        if (x2h > size.width) {
          x2h = size.width;
        }
        if (x2h - x1h >= 1) {
          scaleX.x1 = x1h;
          scaleX.x2 = x2h;
          window.console.log(scaleX);
          const gl = canRef.current.getContext?.('webgl');
          if (gl) {
            const x1H = gl.getUniformLocation(gl.program, 'x1');
            const x2H = gl.getUniformLocation(gl.program, 'x2');
            gl.uniform1f(x1H, x1h);
            gl.uniform1f(x2H, x2h);
          }
        }
      }}
    />
  );
});

Index.defaultProps = {
  className: null,
};

Index.propTypes = {
  className: PropTypes.any,
};

export default Index;
